/*******************************************************************************
filename      VariantType.h
author        Craig Williams

Brief Description:
  A massive file of copy/paste work that is not that fun to look at. Despite
    this, the following class tends to behave like a variant type should
    behave in my mind. Namely, it supports all standard single element
    data types of C++, with string support. Logical conversion and math is
    performed between all the the different data types, with upcasting
    where necessary.

  Variant Types are far slower then the native built in data types; however,
    they are very nice for FileIO, serialization, and scripting.

  All pointers are used as references (except the void pointer). Some
    serialization functions are provided as well.

  Supported Data Types (signed unsigned, and pointers where applicable):
    void *   bool   char    short   int   long    float   double   long double
    CString

  Supported Math Operations:
    ++ -- -  +  *  / = += -= *= /= !  <  >  <= >= == !=

  Conversion operators between any of the specified data types.

  Any pointer can be cast to a void *, otherwise, the pointer type will be
    preserved. IE, you cannot convert a int * to a unsigned int *.

  Helper Functions:
    SetValue_DontChangeType - Copies the value from one variant into anohter
                                without changing the data type of the current
                                variant.
    ConvertTo  - Converts the internal format to the specified type.
    PopPointer - If the variant holds a pointer, it'll duplicate the content
                   that the pointer is pointing at.
    IsAPointer - Returns true if we are working with a pointer
    IsBool     - Returns true if it is a bool
    IsNumeric  - true if char, short, int, long, float, double, or long double
                   with pointer and unsigned variants included
    IsDecimal  - true if char, short, int, or long with pointer and unsigned
                   variants included
    IsFloat    - true if float, double, or long double with pointer variantions
                   included
    IsString   - true if CString or CString *.
    GetType    - Returns that type the variant is holding.
                   See VARIANT_TYPE_* defines below.
    GetTypeString    - Returns the data type as a human readable string
    SetDecimalOutput - Allows you to specify how deciaml values are converted
                         into strings. Namely,
                           DECIAML - base 10 numbers
                           BINARY  - 0b0101010100
                           OCTAL   - 01234567
                           HEX     - 0x123FFF
                         See VARIANT_DECIMAL_OUTPUT_* define below

  Serialization Functions:
    Pack             - Packs the data into a vector of unsigned chars. The
                         stream is signed for type saftey reasons.
                       Warning: Pointers will be directly written to the stream.
                                  They will still be signed though.
    Unpack           - Attempts to unpack a variant type from the specified
                         vector into the current variant type. This function
                         returns the new index to start reading at.
    UnpackType       - Attempts to read the data type of the next element in
                         the vector.
    UnpackTypeString - Same as UnpackType; however, the data type is then
                         converted into a human readable string.

  Debug Functions:
    Print - Dumps the content of the vector to stdout in the following form:
              "<Type>: <Value>\n"
*******************************************************************************/
#ifndef VARIANT_TYPE_H
#define VARIANT_TYPE_H

#include "CString.h"
#include "CVector.h"

  // These defines are used for upcasting. IE, in a math operation,
  //   UNSIGNED_CHAR + CHAR -> CHAR
  //   SHORT + CHAR -> SHORT
  // They also are used to determin the current type of VariantType
  // Note: Unsigned should be before signed variants.
  // Note: PTR variants must be odd and listed before the non-ptr variants.
  //       Non-Ptr variants must be 1 digit above ptr variants.
#define VARIANT_TYPE_NOT_SET             0
#define VARIANT_TYPE_VOID_PTR            1
#define VARIANT_TYPE_VOID                2 // Same as VARIANT_TYPE_NOT_SET
#define VARIANT_TYPE_BOOL_PTR            3
#define VARIANT_TYPE_BOOL                4
#define VARIANT_TYPE_UNSIGNED_CHAR_PTR   5
#define VARIANT_TYPE_UNSIGNED_CHAR       6
#define VARIANT_TYPE_CHAR_PTR            7 // NOT a C Style String Pointer.
#define VARIANT_TYPE_CHAR                8
#define VARIANT_TYPE_UNSIGNED_SHORT_PTR  9
#define VARIANT_TYPE_UNSIGNED_SHORT     10
#define VARIANT_TYPE_SHORT_PTR          11
#define VARIANT_TYPE_SHORT              12
#define VARIANT_TYPE_UNSIGNED_INT_PTR   13
#define VARIANT_TYPE_UNSIGNED_INT       14
#define VARIANT_TYPE_INT_PTR            15
#define VARIANT_TYPE_INT                16
#define VARIANT_TYPE_UNSIGNED_LONG_PTR  17
#define VARIANT_TYPE_UNSIGNED_LONG      18
#define VARIANT_TYPE_LONG_PTR           19
#define VARIANT_TYPE_LONG               20
#define VARIANT_TYPE_FLOAT_PTR          21
#define VARIANT_TYPE_FLOAT              22
#define VARIANT_TYPE_DOUBLE_PTR         23
#define VARIANT_TYPE_DOUBLE             24
#define VARIANT_TYPE_LONG_DOUBLE_PTR    25
#define VARIANT_TYPE_LONG_DOUBLE        26

#define VARIANT_TYPE_CSTRING_PTR        61
#define VARIANT_TYPE_CSTRING            62

#define VARIANT_TYPE_NAMED_VARIANT      0x3F // 63 -- Used during serilization

#define VARIANT_TYPE                    0x3F // 0b00111111
#define VARIANT_DECIMAL_OUTPUT          0xC0 // 0b11000000

  // Format to output a decimal -> CString conversion.
  //   Call VariantType::SetDecimalOutput() to change the output value
#define VARIANT_DECIMAL_OUTPUT_DECIMAL  0x00  // "255"        // Can be negative
#define VARIANT_DECIMAL_OUTPUT_BINARY   0x40  // "0b11111111"
#define VARIANT_DECIMAL_OUTPUT_OCTAL    0x80  // "0377"       // Can be negative
#define VARIANT_DECIMAL_OUTPUT_HEX      0xC0  // "0xFF"

class VariantType
{
  public:
    VariantType(void               );
    VariantType(void           *val);
    VariantType(bool           *val);
    VariantType(bool            val);
    VariantType(char           *val); // NOT a string. Pointer to 1 element
    VariantType(char            val);
    VariantType(unsigned char  *val); // NOT a string. Pointer to 1 element
    VariantType(unsigned char   val);
    VariantType(short          *val);
    VariantType(short           val);
    VariantType(unsigned short *val);
    VariantType(unsigned short  val);
    VariantType(int            *val);
    VariantType(int             val);
    VariantType(unsigned int   *val);
    VariantType(unsigned int    val);
    VariantType(long           *val);
    VariantType(long            val);
    VariantType(unsigned long  *val);
    VariantType(unsigned long   val);
    VariantType(float          *val);
    VariantType(float           val);
    VariantType(double         *val);
    VariantType(double          val);
    VariantType(long double    *val);
    VariantType(long double     val);

    VariantType(const char     *val); // C Style String
    VariantType(const CString  &val);
    VariantType(CString        *val);

    VariantType(const VariantType &rval);
   ~VariantType(void);

    VariantType &operator++(void);        //  Preincrement
    VariantType  operator++(int post);    // Postincrement

    VariantType &operator--(void);        //  Predecrement
    VariantType  operator--(int post);    // Postdecrement

    VariantType  operator- (void) const;  // Negation

      // Upconverts the type, based on the VARIANT_TYPE_* defines listed
      //   above. IE, <char> + <float> will become a float
    VariantType  operator+ (const VariantType &rval) const;
    VariantType  operator- (const VariantType &rval) const;
    VariantType  operator* (const VariantType &rval) const;
    VariantType  operator/ (const VariantType &rval) const;

    VariantType &operator= (const VariantType &rval);

      // Left Hand operand type will not be modified.
    VariantType &operator+=(const VariantType &rval);
    VariantType &operator-=(const VariantType &rval);
    VariantType &operator*=(const VariantType &rval);
    VariantType &operator/=(const VariantType &rval);

      // Comparisions. Upconversion happens when necessary.
      //   IE, (bool)1 != 1.01f   // (bool)1 -> 1.0f
      //       (bool)1 == 1.00f   // (bool)1 -> 1.0f
    bool        operator! (void)                   const;
    bool        operator< (const VariantType &rval) const;
    bool        operator> (const VariantType &rval) const;
    bool        operator<=(const VariantType &rval) const;
    bool        operator>=(const VariantType &rval) const;
    bool        operator==(const VariantType &rval) const;
    bool        operator!=(const VariantType &rval) const;

      // Conversion Operators.
    operator void         *(void) const;
    operator bool          (void) const;
    operator char          (void) const;
    operator unsigned char (void) const;
    operator short         (void) const;
    operator unsigned short(void) const;
    operator int           (void) const;
    operator unsigned int  (void) const;
    operator long          (void) const;
    operator unsigned long (void) const;
    operator float         (void) const;
    operator double        (void) const;
    operator long double   (void) const;

    operator CString       (void) const;

      // Pointer will only be returned if it is the correct type
    operator bool          *(void) const;
    char    *GetCharPtr     (void) const; // operator char *() will break CStr
    operator unsigned char *(void) const;
    operator short         *(void) const;
    operator unsigned short*(void) const;
    operator int           *(void) const;
    operator unsigned int  *(void) const;
    operator long          *(void) const;
    operator unsigned long *(void) const;
    operator float         *(void) const;
    operator double        *(void) const;
    operator long double   *(void) const;
    operator CString       *(void) const;

      // Note: This cannot convert to a pointer type.
    VariantType &ConvertTo(unsigned int type); // Uses VARIANT_TYPE_* defines
      // Changes the value, but not the data type
    VariantType &SetValue_DontChangeType(const VariantType &rval);

      // Converts a VARIANT_TYPE_*_PTR to the non PTR variant. IE, it makes a
      //   copy of the data.
    VariantType  &PopPointer(void);
    bool          IsAPointer(void) const;
    bool          IsBool    (void) const; // bool or bool* only
    bool          IsNumeric (void) const; // decimal || floating point(not bool)
    bool          IsDecimal (void) const; // decimal or decimal *
    bool          IsFloat   (void) const; // float, double, long double + ptrs
    bool          IsString  (void) const;
    bool          IsSigned  (void) const; // Can be negative
    bool          IsNegative(void) const; // Signed and < 0
    unsigned char GetType(void) const;
    CString       GetTypeString(void) const;
    void          SetDecimalOutput(unsigned int type);

      // Serialization functions
    CVector<unsigned char>  Pack(void) const;
    CVector<unsigned char> &Pack(CVector<unsigned char> &data) const;
    unsigned int  Unpack(const CVector<unsigned char> &data, unsigned int start = 0);
    unsigned char UnpackType(const CVector<unsigned char> &data, unsigned int start = 0);
    CString       UnpackTypeString(const CVector<unsigned char> &data, unsigned int start = 0);

      // Debug Related
    void Print(void) const; // Prints out "<Type>: <Value>\n"

  private:
    CString GetTypeString(unsigned char flags) const;
    void BuildString(const CString &val);
    void BuildString(CString *val);
    void DestroyString(void);

    CString OutputDec(long val) const;
    CString OutputDec(unsigned long val) const;

    unsigned char mFlags;

    typedef struct RawStr_str
    {
      CString      *Ptr;
      unsigned char Raw[sizeof(CString)];
    } RawStr_str;

    union
    {
      void           *mVoidPtr;
      bool           *mBoolPtr;
      bool            mBool;
      char           *mCharPtr;
      char            mChar;
      unsigned char  *mUCharPtr;
      unsigned char   mUChar;
      short          *mShortPtr;
      short           mShort;
      unsigned short *mUShortPtr;
      unsigned short  mUShort;
      int            *mIntPtr;
      int             mInt;
      unsigned int   *mUIntPtr;
      unsigned int    mUInt;
      long           *mLongPtr;
      long            mLong;
      unsigned long  *mULongPtr;
      unsigned long   mULong;
      float          *mFloatPtr;
      float           mFloat;
      double         *mDoublePtr;
      double          mDouble;
      long double    *mLDoublePtr;
      long double     mLDouble;

      RawStr_str      mStr;
    } mType;
};

  // Instruct the compiler to pick the "best" conversion.
#define VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(T)        \
VariantType operator+ (T va, const VariantType &var); \
VariantType operator- (T va, const VariantType &var); \
VariantType operator* (T va, const VariantType &var); \
VariantType operator/ (T va, const VariantType &var); \
VariantType operator+ (const VariantType &var, T va); \
VariantType operator- (const VariantType &var, T va); \
VariantType operator* (const VariantType &var, T va); \
VariantType operator/ (const VariantType &var, T va); \
bool        operator< (const VariantType &var, T va); \
bool        operator> (const VariantType &var, T va); \
bool        operator<=(const VariantType &var, T va); \
bool        operator>=(const VariantType &var, T va); \
bool        operator==(const VariantType &var, T va); \
bool        operator!=(const VariantType &var, T va); \
bool        operator< (T va, const VariantType &var); \
bool        operator> (T va, const VariantType &var); \
bool        operator<=(T va, const VariantType &var); \
bool        operator>=(T va, const VariantType &var); \
bool        operator==(T va, const VariantType &var); \
bool        operator!=(T va, const VariantType &var);


VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(bool)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(unsigned char)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(char)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(unsigned short)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(short)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(unsigned int)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(int)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(unsigned long)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(long)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(float)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(double)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(long double)
VARIANT_TYPE_AUTO_CONVERT_PROTOTYPE(const CString &)

#endif
